package cn.jingyuan.owl.web.oauth2;

import cn.jingyuan.bee.utils.ReflectUtils;
import cn.jingyuan.bee.utils.bean.BeanUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.security.oauth2.resource.UserInfoTokenServices;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.OAuth2Request;
import org.springframework.security.oauth2.provider.token.DefaultAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.util.Assert;

import java.util.Collection;
import java.util.Iterator;
import java.util.Map;

/**
 * 认证信息帮助类
 */
public class DefaultOAuth2Helper {

    private final Logger log = LoggerFactory.getLogger(getClass());

    /**
     * 获取认证用户信息
     */
    public static DefaultUserDetails getUser() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

        if (null != authentication && authentication.isAuthenticated() && authentication instanceof OAuth2Authentication) {
            OAuth2Authentication oAuth2Authentication = (OAuth2Authentication) authentication;
            if (oAuth2Authentication.isClientOnly()) {
                OAuth2Request clientToken = oAuth2Authentication.getOAuth2Request();

                DefaultUserDetails userDetails = new DefaultUserDetails();
                userDetails.setClientId(clientToken.getClientId());
                userDetails.setAuthorities(clientToken.getAuthorities());
                return userDetails;
            } else {
                if (authentication.getPrincipal() instanceof DefaultUserDetails) {
                    DefaultUserDetails userDetails = (DefaultUserDetails) authentication.getPrincipal();
                    return userDetails;
                }

                if (authentication.getPrincipal() instanceof Map) {
                    DefaultUserDetails userDetails =
                        BeanUtils.mapToBean((Map) authentication.getPrincipal(), DefaultUserDetails.class, false);
                    return userDetails;
                }
            }
        }

        return null;
    }

    /**
     * 更新用户信息
     *
     * @param tokenStore tokenStore
     * @param userDetails 用户信息
     */
    public static void updateOpenUser(TokenStore tokenStore, DefaultUserDetails userDetails) {
        if (userDetails == null) {
            return;
        }

        Assert.notNull(userDetails.getClientId(), "clientId 不能为空");
        Assert.notNull(userDetails.getUsername(), "username 不能为空");

        // 动态更新客户端生成的 token
        Collection<OAuth2AccessToken> accessTokens = tokenStore.findTokensByClientIdAndUserName(
            userDetails.getClientId(),
            userDetails.getUsername());

        if (null != accessTokens && !accessTokens.isEmpty()) {
            for (OAuth2AccessToken accessToken : accessTokens) {
                // 由于没有 set 方法，使用反射机制强制赋值
                OAuth2Authentication oAuth2Authentication = tokenStore.readAuthentication(accessToken);
                if (null != oAuth2Authentication) {
                    Authentication authentication = oAuth2Authentication.getUserAuthentication();
                    ReflectUtils.setFieldValue(authentication, "principal", userDetails);
                    // 重新保存
                    tokenStore.storeAccessToken(accessToken, oAuth2Authentication);
                }
            }
        }
    }

    /** 更新客户端权限 */
    public static void updateClientAuthorities(
        TokenStore tokenStore,
        String clientId,
        Collection<? extends GrantedAuthority> authorities) {
        if (null == authorities) {
            log.info("Don't do anything, reason: authorities is null");
            return;
        }

        // 动态更新客户端生成的 token
        Collection<OAuth2AccessToken> accessTokens = tokenStore.findTokensByClientId(clientId);
        if (accessTokens != null && !accessTokens.isEmpty()) {
            Iterator<OAuth2AccessToken> iterator = accessTokens.iterator();
            while (iterator.hasNext()) {
                OAuth2AccessToken token = iterator.next();
                OAuth2Authentication oAuth2Authentication = tokenStore.readAuthentication(token);
                if (oAuth2Authentication != null && oAuth2Authentication.isClientOnly()) {
                    // 只更新客户端权限
                    // 由于没有 set 方法，使用反射机制强制赋值
                    ReflectUtils.setFieldValue(oAuth2Authentication, "authorities", authorities);
                    // 重新保存
                    tokenStore.storeAccessToken(token, oAuth2Authentication);
                }
            }
        }
    }

    /** 获取认证用户 id */
    public static Long getUserId() {
        return getUser().getUserId();
    }

    /** 是否拥有权限 */
    public static Boolean hasAuthority(String authority) {
        DefaultUserDetails auth = getUser();
        if (auth == null) {
            return false;
        }
        return AuthorityUtils.authorityListToSet(auth.getAuthorities()).contains(authority);
    }

    /** 构建 token 转换器 */
    public static DefaultAccessTokenConverter buildAccessTokenConverter() {
        DefaultUserDetailsConverter userConverter = new DefaultUserDetailsConverter();

        DefaultAccessTokenConverter tokenConverter = new DefaultAccessTokenConverter();
        tokenConverter.setUserTokenConverter(userConverter);

        return tokenConverter;
    }

    /** 构建用户信息服务类 */
    public static UserInfoTokenServices buildUserInfoTokenServices(DefaultOAuth2ResourceDetails properties) {
        UserInfoTokenServices tokenServices =
            new UserInfoTokenServices(properties.getUserInfoUri(), properties.getClientId());
        tokenServices.setAuthoritiesExtractor(new DefaultAuthoritiesExtractor());
        tokenServices.setPrincipalExtractor(new DefaultPrincipalExtractor());

        return tokenServices;
    }

    /**
     * 构建自定义远程 token 服务类
     *
     * @param properties 配置信息
     *
     * @return {@link DefaultRemoteTokenServices}
     */
    public static DefaultRemoteTokenServices buildRemoteTokenServices(DefaultOAuth2ResourceDetails properties) {
        // 使用自定义系统用户凭证转换器
        DefaultRemoteTokenServices tokenServices = new DefaultRemoteTokenServices();
        tokenServices.setCheckTokenEndpointUrl(properties.getTokenInfoUri());
        tokenServices.setClientId(properties.getClientId());
        tokenServices.setClientSecret(properties.getClientSecret());
        tokenServices.setAccessTokenConverter(buildAccessTokenConverter());

        log.info("buildRemoteTokenServices: {}", tokenServices);

        return tokenServices;
    }

}
